Managing db schema changes without downtime
古いバージョンのコードを実行している古いアプリケーションインスタンスがすべて壊れてしまう危険性がある
本番環境ではデータベーススキーマはキャッシュされており、カラムのドロップや名前の変更を非常に迅速に行うスキーマの変更は、影響を受けるモデルへのすべてのクエリを破壊し、無効なスキーマ例外を発生させる危険性がある
Discourseのアプローチ
マイグレーションに関する情報を追跡できるようにしている
ActiveRecord には schema_migrations というテーブルがあり、実行されたmigrationsの記録があるが情報が少ない
ActiveRecord::Migrationにprependして、欲しい情報を追加している
マイグレーションがいつ実行されたか
移行が実行されるまでにかかった時間
移行が実行されたときに実行されていたRailsのバージョン
モンキーパッチ
危険なマイグレーションの遅延実行
マイグレーションの情報が残っているので、新しいコードが危険なスキーマ変更の後の状態をハンドリングできることを保証してからmigrationするよう遅延(defer)できる
実際にはdb/seedでdrop columnする
この遅延ドロップは、参照している特定のマイグレーションが実行されてから少なくとも30分後(次のマイグレーションサイクルで)に実行される
カラムの名前を変更したい場合は、新しいカラムを作成し、値を新しいカラムに複製し、トリガーを使用して古いカラムを読み取り専用にマークし、古いカラムのドロップを延期します
テーブルを削除したり名前を変更したい場合は、同様のパターンに従う
危険なマイグレーションの実行禁止
PG gemにパッチをあて、dropやrenameのような危険な変更を行おうとすると例外が起きるようにした
上述のプラクティスを強制している
Alternatives
Discourseと似たことをやっているgemたち
マイグレーションのルールを強制するgem
PostgreSQL, MySQL, MariaDB対応
2020-10でもメンテナンスされている
strong_migrationsと似ている
PostgreSQL対応
2018でメンテナンスが止まっている
Rails migrationにタグ付けする
migrationを実行するのがデプロイ前なのか後なのかをコントロールできるようになる
outtriggerに似ている
Railsへの提案
結局すべてRailsに属するものなのでRails本体で危険なマイグレーションの禁止、安全なゼロダウンタイムデプロイのサポートをしてほしい
案
マイグレーションスクリプトにafter_deploy!を記述できる
db:migrate:pre, db:migrate:postコマンドにより、after_deploy!付き(もしくはつかないもの)のスクリプトだけ実行できる
db:migrateはそのまま
Conclusion
安全なゼロダウンタイムデプロイを始めたいなら以下をおすすめする
マイグレーションをデプロイ前または後に実行できるようビルドプロセスを変える
outtrigger, handcuffsを使うのも良い
マイグレーションに強制力をもたせる
strong_migration